package com.demo.common.ratelimit.interceptor;

import com.demo.common.ratelimit.config.RateLimiterProperties;
import com.demo.common.ratelimit.exception.RateLimitException;
import com.demo.common.ratelimit.annotation.ApiRateLimiter;
import com.demo.common.ratelimit.utils.HttpUserInfoUtil;
import com.demo.common.ratelimit.utils.IpAddressUtil;
import com.demo.common.ratelimit.utils.ThreadLocalUtil;
import com.demo.common.redis.component.RedisKeys;
import com.demo.common.redis.component.StringRedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @Author：DayDayUp
 * @Create：2023/2/6 15:34
 */
@Order(2)
@Slf4j
public class UserApiInterceptor implements HandlerInterceptor {

    @Resource
    private StringRedisUtil stringRedisUtil;
    @Resource
    private RateLimiterProperties rateLimiterProperties;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // handler是否为 HandleMethod 实例
        if (handler instanceof HandlerMethod) {
            // 强转
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            String clazzName = handlerMethod.getBeanType().getName();
            // 获取方法
            Method method = handlerMethod.getMethod();
            ApiRateLimiter apiRateLimiter = method.getAnnotation(ApiRateLimiter.class);
            if (apiRateLimiter != null && !apiRateLimiter.limit()) {
                ThreadLocalUtil.setTlBoolean(true);
                return true;
            }

            String username = HttpUserInfoUtil.getSessionUser(request);
            String ip = IpAddressUtil.getIpAddress(request);
            String userAgent = request.getHeader("user-agent");
            if (!StringUtils.hasText(ip) || !StringUtils.hasText(userAgent)){
                throw new RateLimitException("当前请求错误");
            }
            // ip + user + class + method
            String userApiKey;
            if (StringUtils.hasText(username)){
                userApiKey = RedisKeys.userApiLimit(username, clazzName, method.getName());
            }else {
                userApiKey = RedisKeys.userApiLimit(ip, clazzName, method.getName());
            }

            int maxAccessCnt;
            long maxAccessTime;
            // 判断方式是否有AccessLimit注解，有的才需要做限流
            if (apiRateLimiter == null) {
                maxAccessCnt = rateLimiterProperties.getUserApiCount();
                maxAccessTime = rateLimiterProperties.getUserApiTime();
            }else {
                // 获取注解上的内容
                maxAccessCnt = apiRateLimiter.count() == 0 ? rateLimiterProperties.getUserApiCount():apiRateLimiter.count();
                maxAccessTime = apiRateLimiter.time() == 0 ?rateLimiterProperties.getUserApiTime(): apiRateLimiter.time();
            }

            String userApiValue = stringRedisUtil.get(userApiKey);
            if (Objects.isNull(userApiValue)){
                stringRedisUtil.set(userApiKey,"1",maxAccessTime);
            }else{
                int currentCnt = Integer.parseInt(userApiValue) + 1;
                if (currentCnt <= maxAccessCnt){
                    long expire = stringRedisUtil.getExpire(userApiKey);
                    if (expire > 0){
                        stringRedisUtil.set(userApiKey,String.valueOf(currentCnt),expire);
                    }
                }else {
                    throw new RateLimitException("接口访问次数已达上限");
                }
            }
        }
        return true;
    }
}
